module top_ctrl(
input clk,
input rst_b,
//jump from exe
input         jump_en_exe,
input  [31:0] jump_addr_exe,

//jump from irq
input         ini_jump_intp,
input  [31:0] ini_jump_addr_intp,

//clear from irq
input         ini_clear_intp,

//ext hold
input      ext_hold_top,

//for data hazard
input load_exe,
input load_mem,
input [31:0] reg_rdata1_reg,
input [31:0] reg_rdata2_reg,

input [31:0] reg_wdata_exe,
input [31:0] reg_wdata_mem,
input [31:0] reg_wdata_wb_pre,//timing is same to reg_wdata_mem
input [31:0] reg_wdata_wb,

input reg_ren1_dec,
input reg_ren2_dec,

input [4:0] reg_raddr1_dec,
input [4:0] reg_raddr2_dec,

input reg_wen_exe,
input reg_wen_mem,
input reg_wen_wb,

input [4:0] reg_waddr_exe,
input [4:0] reg_waddr_mem,
input [4:0] reg_waddr_wb,

output reg [31:0] reg_rdata1_ctl,
output reg [31:0] reg_rdata2_ctl,

//for bus arbiter
input [31:0]  pc_if_pre,
input         pc_req_if_pre,
output [31:0] inst_if_ctl,

input [31:0]  mem_addr_mem_pre,
input         mem_cs_en_mem_pre,
input         mem_wen_mem_pre,
output [31:0] mem_rdata_mem_ctl,

input [31:0]  mem_addr_wb_pre,
input [31:0]  mem_wdata_wb_pre,
input         mem_cs_en_wb_pre,
input         mem_wen_wb_pre,

output [31:0]  mem_addr_ctl,
output [31:0]  mem_wdata_ctl,
output         mem_cs_en_ctl,
output         mem_wen_ctl,
input   [31:0] mem_rdata_top,

//output
output reg hold_if_ctl,
output reg hold_dec_ctl,
output reg hold_exe_ctl,
output reg hold_mem_ctl,
output reg hold_wb_ctl,

output reg clear_if_ctl,
output reg clear_dec_ctl,
output reg clear_exe_ctl,
output reg clear_mem_ctl,
output reg clear_wb_ctl,

output reg        jump_if_ctl,
output reg [31:0] jump_addr_if_ctl

);

//reg data out
wire reg_rdata1_from_exe_flg = (reg_raddr1_dec == reg_waddr_exe) & (reg_raddr1_dec != 5'd0) & reg_ren1_dec & reg_wen_exe & ~load_exe;  
wire reg_rdata2_from_exe_flg = (reg_raddr2_dec == reg_waddr_exe) & (reg_raddr2_dec != 5'd0) & reg_ren2_dec & reg_wen_exe & ~load_exe;  

wire reg_rdata1_from_mem_flg = (reg_raddr1_dec == reg_waddr_mem) & (reg_raddr1_dec != 5'd0) & reg_ren1_dec & reg_wen_mem & ~load_mem;  
wire reg_rdata2_from_mem_flg = (reg_raddr2_dec == reg_waddr_mem) & (reg_raddr2_dec != 5'd0) & reg_ren2_dec & reg_wen_mem & ~load_mem;  

wire reg_rdata1_from_wb_pre_flg = (reg_raddr1_dec == reg_waddr_mem) & (reg_raddr1_dec != 5'd0) & reg_ren1_dec & reg_wen_mem & load_mem;  
wire reg_rdata2_from_wb_pre_flg = (reg_raddr2_dec == reg_waddr_mem) & (reg_raddr2_dec != 5'd0) & reg_ren2_dec & reg_wen_mem & load_mem;

wire reg_rdata1_from_wb_flg = (reg_raddr1_dec == reg_waddr_wb) & (reg_raddr1_dec != 5'd0) & reg_ren1_dec & reg_wen_wb;  
wire reg_rdata2_from_wb_flg = (reg_raddr2_dec == reg_waddr_wb) & (reg_raddr2_dec != 5'd0) & reg_ren2_dec & reg_wen_wb;  

always @(*) begin
  if(reg_rdata1_from_exe_flg)
    reg_rdata1_ctl = reg_wdata_exe;
  else if(reg_rdata1_from_mem_flg)
    reg_rdata1_ctl = reg_wdata_mem;
  else if(reg_rdata1_from_wb_pre_flg)
    reg_rdata1_ctl = reg_wdata_wb_pre;
  else if(reg_rdata1_from_wb_flg)
    reg_rdata1_ctl = reg_wdata_wb;
  else
    reg_rdata1_ctl = reg_rdata1_reg;
end

always @(*) begin
  if(reg_rdata2_from_exe_flg)
    reg_rdata2_ctl = reg_wdata_exe;
  else if(reg_rdata2_from_mem_flg)
    reg_rdata2_ctl = reg_wdata_mem;
  else if(reg_rdata2_from_wb_pre_flg)
    reg_rdata2_ctl = reg_wdata_wb_pre;
  else if(reg_rdata2_from_wb_flg)
    reg_rdata2_ctl = reg_wdata_wb;
  else
    reg_rdata2_ctl = reg_rdata2_reg;
end

//bus arbiter
wire if_access_bus_en  = ~mem_cs_en_wb_pre & ~mem_cs_en_mem_pre & pc_req_if_pre;
wire mem_access_bus_en = ~mem_cs_en_wb_pre & mem_cs_en_mem_pre;
wire wb_access_bus_en  = mem_cs_en_wb_pre;

reg if_access_bus_en_d,mem_access_bus_en_d;
always @(posedge clk or negedge rst_b) begin
  if(~rst_b) begin
    if_access_bus_en_d  <= 1'b0;
    mem_access_bus_en_d <= 1'b0;
  end
  else begin
    if_access_bus_en_d  <= if_access_bus_en;
    mem_access_bus_en_d <= mem_access_bus_en;
  end
end

assign mem_addr_ctl = mem_cs_en_wb_pre ? mem_addr_wb_pre : (mem_cs_en_mem_pre ? mem_addr_mem_pre : pc_if_pre);
assign mem_wdata_ctl = mem_cs_en_wb_pre ? mem_wdata_wb_pre : 32'd0;
assign mem_cs_en_ctl = mem_cs_en_wb_pre | mem_cs_en_mem_pre | pc_req_if_pre;
assign mem_wen_ctl = mem_cs_en_wb_pre ? mem_wen_wb_pre : (mem_cs_en_mem_pre ? mem_wen_mem_pre : 1'b0);

assign inst_if_ctl = mem_rdata_top & {32{if_access_bus_en_d}};
assign mem_rdata_mem_ctl = mem_rdata_top & {32{mem_access_bus_en_d}};

wire bus_mem_en_wb_en = mem_cs_en_wb_pre & mem_cs_en_mem_pre;
wire bus_if_en_mem_or_wb_en = pc_req_if_pre & (mem_cs_en_wb_pre | mem_cs_en_mem_pre);

//hold & clear out
//data hazard hold
wire exe_rs_mem_ld_rd_en = ((reg_raddr1_dec == reg_waddr_exe) & (reg_raddr1_dec != 5'd0) & reg_ren1_dec & reg_wen_exe & load_exe) |
                           ((reg_raddr2_dec == reg_waddr_exe) & (reg_raddr2_dec != 5'd0) & reg_ren2_dec & reg_wen_exe & load_exe) ;


always @(*) begin
hold_if_ctl      = 1'd0;
hold_dec_ctl     = 1'd0;
hold_exe_ctl     = 1'd0;
hold_mem_ctl     = 1'd0;
hold_wb_ctl      = 1'd0;

clear_if_ctl     = 1'd0;
clear_dec_ctl    = 1'd0;
clear_exe_ctl    = 1'd0;
clear_mem_ctl    = 1'd0;
clear_wb_ctl     = 1'd0;

jump_if_ctl      = 1'd0;
jump_addr_if_ctl = 32'd0;  

if(ext_hold_top) begin
  hold_if_ctl        = 1'd1;
  hold_dec_ctl       = 1'd1;
  hold_exe_ctl       = 1'd1;
  hold_mem_ctl       = 1'd1;
  hold_wb_ctl       = 1'd1;
end
if(ini_jump_intp) begin
  jump_if_ctl      = 1'd1;
  jump_addr_if_ctl = ini_jump_addr_intp;
  clear_if_ctl    = 1'd1;
  clear_dec_ctl    = 1'd1;
  clear_exe_ctl    = 1'd1;
end
else if(ini_clear_intp & bus_mem_en_wb_en) begin
  clear_if_ctl      = 1'd1;
  clear_dec_ctl    = 1'd1;
  hold_exe_ctl    = 1'd1;
  clear_mem_ctl    = 1'd1;
end
else if(ini_clear_intp) begin
  clear_if_ctl      = 1'd1;
  clear_dec_ctl    = 1'd1;
  clear_exe_ctl    = 1'd1;
end
else if(bus_mem_en_wb_en) begin
  hold_if_ctl    = 1'd1;
  hold_dec_ctl    = 1'd1;
  hold_exe_ctl    = 1'd1;
  clear_mem_ctl    = 1'd1;
end
else if(jump_en_exe) begin
  jump_if_ctl      = 1'd1;
  jump_addr_if_ctl = jump_addr_exe;
  clear_if_ctl    = 1'd1;
  clear_dec_ctl    = 1'd1;
  clear_exe_ctl    = 1'd1;
end
else if(exe_rs_mem_ld_rd_en) begin
  hold_if_ctl      = 1'd1;
  hold_dec_ctl     = 1'd1;
  clear_exe_ctl    = 1'd1;
end
else if(bus_if_en_mem_or_wb_en) begin
  hold_if_ctl      = 1'd1;
  clear_dec_ctl     = 1'd1;
end

end//always


endmodule
